(ns poker (:gen-class)
 (:use hiccup.core
	hiccup.form-helpers
	hiccup.page-helpers
	compojure.core
	compojure.response
	clojure.contrib.str-utils
        ring.adapter.jetty)
  (:require [compojure.route :as route]
            [org.danlarkin.json :as json])
)

(def suits #{:hearts, :diamonds, :clubs, :spades})

(def names #{:two, :three, :four, :five, :six,
             :seven, :eight, :nine, :ten, :jack,
             :queen, :king, :ace})

(defstruct card :suit :name)

(def deck
     (for [suit suits, name names]
       (struct-map card :suit suit :name name)))


;;;;;deck test;;;;;;;;;;;;;;

(count deck)
;; 52

(first deck)
;; {:suit :hearts, :name :queen}

(last deck)
;; {:suit :clubs, :name :four}

(defn heart? [card]
  (= :hearts (card :suit)))

(count (filter heart? deck))
;; 13

(defn king? [card]
  (= :king (card :name)))
(count (filter king? deck))
;; 4

(defn king-of-hearts? [card]
  (and (king? card) (heart? card)))

(filter king-of-hearts? deck)
;; ({:suit :hearts, :name :king})

;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(defn random-card [deck]
  (nth (seq deck)
       (. (new java.util.Random)
          nextInt
          (count deck))))

(defn shuffle
  ([deck]
     (shuffle [] deck))
  ([clean dirty]
     (if (empty? dirty)
       clean
       (let [pick (random-card dirty)]
         (shuffle (conj clean pick)
                  (disj (set dirty) pick))))))

(defn build-property-histogram [cards property-name property-pool]
  (reduce
   (fn [sieve value]
     (conj sieve
           { value
            (count (filter 
                    (fn [card] (= value (card property-name)))
                    cards)) }))
   {}
   property-pool))

(defn straight? [hand]
  (some
   (fn [straight] (= (set (map :name hand)) (set straight)))
   (partition 5 1 [:ace, :two, :three, :four, :five, :six, :seven, :eight,
                   :nine, :ten, :jack, :queen, :king, :ace])))

(defn rank-cards [cards]
  (let [suit-histo (build-property-histogram cards :suit suits)
        value-histo (build-property-histogram cards :name names)
        max-per-suit (apply max (vals suit-histo))
        max-per-value (apply max (vals value-histo))]
    (cond
      (= 4 max-per-value)
          :quads
      
      (= 3 max-per-value)
          (if (some #(= 2 %) (vals value-histo))
              :full-house
              :trips)

      (= 2 max-per-value)
          (if (< 1 (count (filter #(= 2 %) (vals value-histo))))
              :two-pair
              :pair)

      (and (straight? cards) (> 5 max-per-suit))
          :straight

      (and (not (straight? cards)) (= 5 max-per-suit))
          :flush

      (= 5 max-per-suit)
          (if (= #{:ace, :king, :queen, :jack, :ten} (set (map :name cards)))
              :royal-flush
              :straight-flush)

      true
          :high-card)))

(defn rand-cards-toJSON []
 (let [rnd-hnd (take 5 (shuffle deck))]
      (str "[" (rank-cards rnd-hnd) "]|[" (apply str rnd-hnd) "]")))

(defn construct-gui []
  (html 
    (doctype :html4) 
    [:html 
      [:head 
        [:title "Poker Application v1.0 - by Srdjan"]
        (include-js "js/ultimateAjax.js")
        (include-js "js/JSON2.js")
        (include-js "js/jquery-1.4.2.min.js")
        (include-js "js/jquery-ui-1.8.5.custom.min.js")
        (include-js "js/ui.selectmenu.js")
        (include-js "js/jquery.flip.min.js")
        (include-js "js/pokerApp.js")
        (include-css "css/jqueryui/jquery-ui.css")
        (include-css "css/cards.css")
        (include-css "css/styles.css")
        ] 
      [:body 
	[:div {:id "header-image"}]
	[:div {:id "header-image-right"}]
	[:div {:id "header-image-left"}]
	[:div {:id "main-border"}
		[:div {:id "score-board"}
			[:div {:id "score-board-left"}]
			[:div {:id "score-board-right"}]
			[:div {:id "score-board-left-bottom"}]
			[:div {:id "control-buttons"}
				[:div {:id "betselectbox"}
					[:select {:name "speedA" :id "speedA"}
						[:option {:value "high-card"} "high-card"]
						[:option {:value "pair"} "pair"]
						[:option {:value "two-pair"} "two-pair"]
						[:option {:value "trips"} "trips"]
						[:option {:value "straight"} "straight"]
						[:option {:value "flush"} "flush"]
						[:option {:value "straight-flush"} "straight-flush"]
						[:option {:value "royal-flush"} "royal-flush"]
					]
				]
				[:input {:id "button-custom" :class "deal" :type "button" :value "Deal"}]
				[:input {:id "button-custom" :class "minus" :type "button" :value "-"}]
				[:input {:id "button-custom" :class "plus" :type "button" :value "+"}]
			]
			[:div {:id "card-container" :class "playingCards fourColours faceImages rotateHand"}
				[:ul {:class "playing-cards"}
					[:li {:id "c0"}
						[:div {:class "card back"}]
					]
					[:li {:id "c1"}
						[:div {:class "card back"}]
					]
					[:li {:id "c2"}
						[:div {:class "card back"}]
					]
					[:li {:id "c3"}
						[:div {:class "card back"}]
					]
					[:li {:id "c4"}
						[:div {:class "card back"}]
					]
				]
			]
		]
	]
	[:div {:id "header-image-bottom"}]
       ]
]
    )
)

;;; Routes
	   

(defroutes webservice
  (GET "/" [] (construct-gui))
  (route/files "/" {:root "public"}) 
  (GET "/test" [] (rand-cards-toJSON))
)

(defn -main [& args]
(run-jetty webservice {:port 1234}))
